package cn.keniykey.mc.viewlibrary.channel;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.bukkit.entity.Player;

import cn.keniykey.mc.viewlibrary.reflection.ReflectionClass;
import cn.keniykey.mc.viewlibrary.reflection.ReflectionField;
import cn.keniykey.mc.viewlibrary.reflection.ReflectionMethod;
import cn.keniykey.mc.viewlibrary.reflection.ReflectionUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;

public final class ChannelUtilV1_16_3 extends AbChannelUtil{
	private final ReflectionMethod method_bukkit_craftplayer_gethandle,method_nms_playerconnection_sendpacket;
	private final ReflectionField field_nms_entityplayer_playerconnection;
	private final ReflectionField field_nms_playerconnection_networkmanager,field_nms_networkmanager_channel;
	
	private final ExecutorService service_add = Executors.newSingleThreadExecutor();
	private final ExecutorService service_remove = Executors.newSingleThreadExecutor();
	
	ChannelUtilV1_16_3() {
		this.method_bukkit_craftplayer_gethandle = ReflectionUtil.findReflectionClass("cb,entity.CraftPlayer").getMethod("getHandle");
		final ReflectionClass class_nms_playerconnection = ReflectionUtil.findReflectionClass("nms,PlayerConnection");
		this.field_nms_entityplayer_playerconnection = ReflectionUtil.findReflectionClass("nms,EntityPlayer").findField(class_nms_playerconnection);
		this.method_nms_playerconnection_sendpacket = class_nms_playerconnection.findMethodByName("sendPacket");
		final ReflectionClass class_nms_networkmanager = ReflectionUtil.findReflectionClass("nms,NetworkManager");
		this.field_nms_playerconnection_networkmanager = class_nms_playerconnection.findField(class_nms_networkmanager);
		this.field_nms_networkmanager_channel = class_nms_networkmanager.findField(Channel.class);
	}

	@Override
	public void addPlayerAbPacketReceiveHandler(Player player, AbPacketReceiveHandler handler) {
		handler.setPlayer(player);
		this.service_add.submit( () -> {
			final Channel channel = this.getPlayerChannel(player);
			if(channel.pipeline().get(CHANNEL_NAME) != null) channel.pipeline().remove(CHANNEL_NAME);
			channel.pipeline().addBefore(CHANNEL_MAIN, CHANNEL_NAME, new PacketChannelHandler(handler));
		});
	}
	
	private final class PacketChannelHandler extends ChannelDuplexHandler{
		private final AbPacketReceiveHandler handler;
		PacketChannelHandler(AbPacketReceiveHandler handler1) {
			this.handler = handler1;
		}
		@Override
		public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
			if(this.handler.onServerReceivePacket(msg)) return;
			super.channelRead(ctx, msg);
		}
	}
	
	@Override
	public void removePlayerAbPacketReceiveHandler(Player player) {
		this.service_remove.submit( () -> {
			final Channel channel = this.getPlayerChannel(player);
			if(channel.pipeline().get(CHANNEL_NAME) != null) channel.pipeline().remove(CHANNEL_NAME);
		});
	}
	
	@Override
	public void sendPacket(Player player, Object packet) {
		final Object connection = this.getConnection(player);
		this.method_nms_playerconnection_sendpacket.dynamicInvoke(connection, packet);
	}
	private Object getConnection(Player player) {
		final Object player_nms = this.method_bukkit_craftplayer_gethandle.dynamicInvoke(player);
		return this.field_nms_entityplayer_playerconnection.dynamicGet(player_nms);
	}
	private Channel getPlayerChannel(Player player) {
		final Object connection = this.getConnection(player);
		final Object networkmanager = this.field_nms_playerconnection_networkmanager.dynamicGet(connection);
		return (Channel) this.field_nms_networkmanager_channel.dynamicGet(networkmanager);
	}
}